/*
 * 23/10/2011 - 13:03
 */
package org.nettyflux.tcp;

import org.nettyflux.netmessages.NetworkMessage;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import org.nettyflux.communicator.ListeningThread;
import org.nettyflux.communicator.Receiver;
import org.nettyflux.mailbox.MailBox;
import org.nettyflux.mailbox.ReceiveListener;

/**
 * @author Victor Williams Stafusa da Silva
 */
public class TCPServerCommunicator implements Receiver<TCPConnectionAccepted> {

    private final ServerSocket socket;
    private final MailBox<NetworkMessage> mailbox;
    private final TCPServerListener listener;

    public TCPServerCommunicator(MailBox<NetworkMessage> mailbox, int port) throws IOException {
        this(mailbox, new ServerSocket(port));
    }

    public TCPServerCommunicator(MailBox<NetworkMessage> mailbox, ServerSocket socket) throws IOException {
        this.socket = socket;
        this.mailbox = mailbox;
        this.listener = new TCPServerListener();
        this.mailbox.addReceiveListener(TCPServerCloseConnectionCommand.class, listener);
        ListeningThread.create(this);
    }

    public ServerSocket getSocket() {
        return socket;
    }

    @Override
    public TCPConnectionAccepted receive() throws IOException {
        if (isClosed()) throw new IllegalStateException("The socket is closed.");

        Socket clientSocket = socket.accept();
        TCPClientCommunicator comm = new TCPClientCommunicator(mailbox, clientSocket, true);
        TCPConnectionAccepted acc = new TCPConnectionAccepted(comm.getClient(), clientSocket);
        mailbox.postMessage(acc);
        return acc;
    }

    @Override
    public void close() throws IOException {
        mailbox.removeReceiveListener(listener);
        socket.close();
    }

    @Override
    public boolean isClosed() {
        return socket.isClosed();
    }

    private class TCPServerListener implements ReceiveListener<TCPServerCloseConnectionCommand> {

        @Override
        public void receive(TCPServerCloseConnectionCommand message) {
            if (message.getPort() == socket.getLocalPort()) {
                try {
                    close();
                    mailbox.postMessage(message.makeAck());
                } catch (IOException e) {
                    mailbox.postMessage(message.makeFailAck(e));
                }
            }
        }
    }
}
